#include <fstream>
#include "QueryLoader.h"

extern double CPU_speed;
extern double RAM_speed;
extern double HDD_access_time;
extern double NET_transmission_time;
extern double Nodes_count;

extern std::vector <Operator *> LogicalOperators;
extern std::vector <PhysicalOperator *> PhysicalOperators;

QueryLoader::QueryLoader()
{
}

Query *QueryLoader::loadQueryFromFile(std::string filename)
{
	Query *q = 0;

	std::ifstream ifile(filename.c_str());

	if (!ifile)
	{
		return q;
	}
		
	Json::Reader reader;
	Json::Value root;

	bool read = reader.parse(ifile, root);

	ifile.close();

	if (read)
	{
		parseLogicalOps(root["logical_operators"]);	
		parsePhysicalOps(root["physical_operators"]);
		parseSysConf(root["system_configuration"]);
		q = parseQuery(root["query"], q);
	}
	else
	{
		std::cout << "Error reading the input file.\nPlease check its syntax according to JSON rules.";
	}

	return q;
}

void QueryLoader::parseLogicalOps(Json::Value &root)
{
	LogicalOperators.push_back(new Operator(EMPTY_OPERATOR));

	for (Json::Value::iterator i = root.begin(); i != root.end(); i++)
	{
		if ((*i).isObject())
		{
			LogicalOperators.push_back(new Operator(i.memberName()));			
		}
	}

	for (Json::Value::iterator i = root.begin(); i != root.end(); i++)
	{
		if ((*i).isObject())
		{			
			Operator *op = findLogicalOperatorbyName(i.memberName());

			if ((*i)["commutativity"] == true)
			{
				op->setOperatorProperties(PROPERTY_COMMUTATIVITY);

				Json::Value ops = (*i)["opsComm"];

				std::vector <Operator *> opsComm;
				for (Json::Value::iterator i = ops.begin(); i != ops.end(); i++)
				{
					opsComm.push_back(findLogicalOperatorbyName((*i).asString()));
				}

				op->addCommOps(opsComm);
			}

			if ((*i)["idempotency"] == true)
			{
				op->setOperatorProperties(PROPERTY_IDEMPOTENCY);
			}

			if ((*i)["not_movable"] == true)
			{
				op->setOperatorProperties(PROPERTY_NOT_MOVABLE);
			}

			if ((*i)["distributivity"] == true)
			{
				op->setOperatorProperties(PROPERTY_DISTRIBUTIVITY);
			}

			//NOTE: Add here more properties.
		}
	}
}

Query *QueryLoader::parseQuery(Json::Value &root, Query *q)
{
	int curr = 0;
	std::vector<std::pair<OperatorNode *, std::string> > deps;

	for (Json::Value::iterator i = root.begin(); i != root.end(); i++)
	{
		if ((*i).isObject() && strcmp(i.memberName(), "array"))
		{
			Operator *op = findLogicalOperatorbyName(i.memberName());
			if (!op)
			{
					op = findLogicalOperatorbyName(EMPTY_OPERATOR);
			}

			Query *t;

			if (!q)
			{
				q = new Query(new OperatorNode(op, 0));
				t = q;
			}
			else
			{
				q->addOperator(new OperatorNode(op, 0));
				t = q->lists[curr++];
			}

			Json::Value::Members m = (*i).getMemberNames();

			if (!m.empty())
			{				
				for (Json::Value::Members::iterator j = m.begin(); j != m.end(); j++)
				{
					if ((*j) == "array")
					{
						Array *arr = parseArray((*i)[*j]);

						std::vector <PhysicalOperator *> list;
						findPhysicalOperatorbyName(i.memberName(), list);

						if (list.empty())
						{
							Operator *op = findLogicalOperatorbyName(i.memberName());
							PhysicalOperators.push_back(new PhysicalOperator(i.memberName(), op, new OperatorCost(arr)));
							t->node->physOp = *(PhysicalOperators.end() - 1);
						}
						else
						{
							t->node->physOp = list.front();
							t->node->physOp->cost->setArray(arr);
						}
					}								
					else if ((*j) == "dependencies")
					{
						Json::Value dep = (*i)[*j];

						for (Json::Value::iterator i = dep.begin(); i != dep.end(); i++)
						{
							deps.push_back(std::make_pair(t->node, (*i).asCString()));
						}
					}
					else if ((*j) == "label")
					{
						t->node->label = (*i)["label"].asString();
					}
					else if ((*j) == "operator")
					{
						t->node->op = findLogicalOperatorbyName((*i)["operator"].asString());

						if (t->node->label == "")
						{
							t->node->label = i.memberName();
						}
					}
				}		
			}

			parseQuery(*i, t);
		}
	}

	for (std::vector<std::pair<OperatorNode *, std::string> > :: iterator i = deps.begin(); i != deps.end(); ++i)
	{
		(*i).first->dependencies = new Dependencies;

		(*i).first->dependencies->push_back(findOperatorNode(q, (*i).second));
	}

	return q;
}

void QueryLoader::parsePhysicalOps(Json::Value &root)
{
	PhysicalOperators.push_back(new PhysicalOperator(EMPTY_OPERATOR, findLogicalOperatorbyName(EMPTY_OPERATOR)));

	for (Json::Value::iterator i = root.begin(); i != root.end(); i++)
	{
		if ((*i).isObject())
		{
			for (Json::Value::iterator j = (*i).begin(); j != (*i).end(); j++)
			{
				if ((*j).isObject())
				{
					PhysicalOperators.push_back(new PhysicalOperator(j.memberName(), findLogicalOperatorbyName(i.memberName()), parseCost(*j)));
				}
				else
				{
					PhysicalOperators.push_back(new PhysicalOperator(i.memberName(), findLogicalOperatorbyName(i.memberName()), parseCost(*i)));
					break;
				}
			}
		}
	}
}

Array *QueryLoader::parseArray(Json::Value &root)
{
	Json::Value::Members m = root.getMemberNames();

	assert(!m.empty());

	std::vector <int> dimensions;
	double empty = 1;

	int chunks = 1;
	int chunkSize = 1;
	int attributes = 1;
	double density = 1;
	std::vector <int> attributeSize;

	for (Json::Value::Members::iterator j = m.begin(); j != m.end(); j++)
	{
		if ((*j) == "count")
		{
			Json::Value d = root[*j];

			for (Json::Value::iterator i = d.begin(); i != d.end(); i++)
			{
				dimensions.push_back((*i).asInt());
			}
		}
		else if ((*j) == "empty")
		{
			empty = root[*j].asDouble();
		}
		else if ((*j) == "chunks")
		{
			chunks = root[*j].asInt();
		}
		else if ((*j) == "chunk_size")
		{
			chunkSize = root[*j].asInt();
		}
		else if ((*j) == "attributes")
		{
			attributes = root[*j].asInt();
		}
		else if ((*j) == "density")
		{
			density = root[*j].asDouble();
		}
		else if ((*j) == "attribute_size")
		{
			Json::Value d = root[*j];

			for (Json::Value::iterator i = d.begin(); i != d.end(); i++)
			{
				attributeSize.push_back((*i).asInt());
			}
		}
	}

	return new ChunkArray(chunks, chunkSize, density, attributeSize);
	//return new SimpleArray(dimensions, empty);
}

Cost *QueryLoader::parseCost(Json::Value &root)
{
	Json::Value::Members m = root.getMemberNames();

	if (m.empty())
		return 0;		

	Array *arr = 0;
	double time = 0;
	double coefficient = 1;

	for (Json::Value::Members::iterator j = m.begin(); j != m.end(); j++)
	{
		if ((*j) == "array")
		{
			arr = parseArray(root[*j]);
		}
		else if ((*j) == "time")
		{
			time = root[*j].asDouble();
		}
		else if ((*j) == "coefficient")
		{
			coefficient = root[*j].asDouble();
		}
		else if ((*j) == "CPU_func")
		{
			;
		}
		else if ((*j) == "RAM_func")
		{
			;
		}
	}

	return new OperatorCost(time, coefficient, arr ? arr : 0);
}

void QueryLoader::parseSysConf(Json::Value &root)
{
	Json::Value::Members m = root.getMemberNames();

	for (Json::Value::Members::iterator i = m.begin(); i != m.end(); i++)
	{
		if ((*i) == "HDD_access_time")
		{
			HDD_access_time = root[*i].asDouble();
		}
		else if ((*i) == "NET_transmission_time")
		{
			NET_transmission_time = root[*i].asDouble();
		}
		else if ((*i) == "nodes")
		{
			Nodes_count = root[*i].asInt();
		}
		else if ((*i) == "CPU_speed")
		{
			CPU_speed = root[*i].asDouble();
		}
		else if ((*i) == "RAM_speed")
		{
			RAM_speed = root[*i].asDouble();
		}
	}
}